package chessboardpkg;

import javax.swing.JPanel;
import javax.swing.BorderFactory;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.ArrayList;
import java.awt.Rectangle;
import java.awt.Dimension;

public class ChessBoard extends JPanel {

	/**
	 * 
	 */
	private static final long serialVersionUID = 6781737628074607815L;
	private ArrayList<Rectangle> AllRects;
	private ArrayList<Rectangle> BlackRects;
	private ArrayList<Rectangle> WhiteRects;
	private int SquareSize;
	private int XOffset;
	private int YOffset;
	private Rectangle MouseOverRectangle;
	private Rectangle FirstClickRectangle;
	private ArrayList<ChessEventListener> mCEL;

	public ChessBoard() {
		super(false);
		
		this.setBorder(BorderFactory.createLineBorder(Color.BLACK));
		this.MouseOverRectangle = null;
		this.FirstClickRectangle = null;
		this.mCEL = new ArrayList<ChessEventListener>();
		
		this.addMouseListener(new MouseAdapter(){public void mousePressed(MouseEvent e)
				{
					if(e.getButton() == MouseEvent.BUTTON1)
					{
						OnClick(e.getPoint());
					}
				}
		});
		
		this.addMouseMotionListener(new MouseAdapter(){public void mouseMoved(MouseEvent e)
				{
					OnMove(e.getPoint());
				}
		});
		
		this.addMouseMotionListener(new MouseAdapter(){public void mouseExited(MouseEvent e)
		{
			OnMouseLeave();
		}
		});
		
		this.addComponentListener(new ComponentAdapter(){public void componentResized(ComponentEvent e)
		{
			OnResize();
		}
		});
		
		this.setOpaque(false);
		
		//Initialize the board
		this.OnResize();
	}
	
	public void addChessEventListener(ChessEventListener e)
	{
		this.mCEL.add(e);
	}
	
	public void removeChessEventListener(ChessEventListener e)
	{
		this.mCEL.remove(e);
	}

	protected void OnMouseLeave() {
		this.MouseOverRectangle = null;
		this.repaint();
	}

	protected void OnResize() {
		this.CalcSquares();
		//this.repaint();
	}

	protected void OnMove(Point point) {
		for(int i = 0; i < this.AllRects.size(); i++)
		{
			if(this.AllRects.get(i).contains(point))
			{
				this.MouseOverRectangle = new Rectangle(this.AllRects.get(i).x, this.AllRects.get(i).y, this.AllRects.get(i).width, this.AllRects.get(i).height);
				break;
			}
			
			this.MouseOverRectangle = null;
		}
		this.repaint();
	}
	
	private Rectangle getContainerRectangle(Point p)
	{
		for(int i = 0; i < this.AllRects.size(); i++)
		{
			if(this.AllRects.get(i).contains(p))
				return this.AllRects.get(i);
		}
		
		return null;
	}

	protected void OnClick(Point point) {
		Rectangle temp = null;
		
		if((temp = this.getContainerRectangle(point)) != null)
		{
			if(this.FirstClickRectangle == null)
			{
			BoardEventArgs e = new BoardEventArgs(BoardEventType.SQUARESELECT, new Point((temp.x - this.XOffset)/this.SquareSize, (temp.y - this.YOffset) / this.SquareSize));
			this.FireBoardEvent(e);
			
			if(e.getmAllowAction())
				this.FirstClickRectangle = temp;
			}
			else if(temp == this.FirstClickRectangle)
				this.FirstClickRectangle = null;
			
			this.repaint();
		}
		
	}
	
	protected void FireBoardEvent(BoardEventArgs e)
	{
		for(int i = 0; i < this.mCEL.size(); i++)
			this.mCEL.get(i).BoardEventHappened(e);
	}
	
	protected void paintComponent(Graphics g)
	{
		Graphics2D g2 = (Graphics2D)g;
		super.paintComponent(g2);
		this.DrawSquares(g2);
		this.DrawMouseOver(g2);
		this.DrawClickRectangle(g2);
	}
	
	private void DrawClickRectangle(Graphics2D g)
	{
		if(this.FirstClickRectangle != null)
		{
			g.setColor(Color.BLUE);
			g.setStroke(new BasicStroke(5));
			g.drawRect(this.FirstClickRectangle.x, this.FirstClickRectangle.y, this.FirstClickRectangle.width, this.FirstClickRectangle.height);
		}
	}
	
	private void DrawMouseOver(Graphics2D g)
	{
		if(this.MouseOverRectangle != null)
		{
			g.setColor(Color.YELLOW);
			g.setStroke(new BasicStroke(5));
			g.drawRect(this.MouseOverRectangle.x, this.MouseOverRectangle.y, this.MouseOverRectangle.width, this.MouseOverRectangle.height);
		}
	}
	
	private void DrawSquares(Graphics g)
	{
		g.setColor(Color.BLACK);
		
		for(int i = 0; i < this.AllRects.size(); i++)
			g.drawRect(this.AllRects.get(i).x, this.AllRects.get(i).y, this.AllRects.get(i).width, this.AllRects.get(i).height);
		
		for(int i = 0; i < this.BlackRects.size(); i++)
			g.fillRect(this.BlackRects.get(i).x, this.BlackRects.get(i).y, this.BlackRects.get(i).width, this.BlackRects.get(i).height);
		
		g.setColor(Color.WHITE);
		
		for(int i = 0; i < this.WhiteRects.size(); i++)
			g.fillRect(this.WhiteRects.get(i).x, this.WhiteRects.get(i).y, this.WhiteRects.get(i).width, this.WhiteRects.get(i).height);
	}
	
	private void CalcSquares()
	{
		ArrayList<Rectangle> temp = new ArrayList<Rectangle>();
		ArrayList<Rectangle> tempB = new ArrayList<Rectangle>();
		ArrayList<Rectangle> tempW = new ArrayList<Rectangle>();
		
		//Calculate SquareSize and offsets
		this.SquareSize = ((this.getWidth() > this.getHeight())? this.getHeight() / 8 - 20 : this.getWidth() / 8);
		
		this.XOffset = ((this.getWidth() > this.getHeight()) ? ((this.getWidth() - 8 * SquareSize) / 2) : 0);
		this.YOffset = ((this.getWidth() > this.getHeight()) ? 0 : ((this.getHeight() - 8 * SquareSize) / 2));
		
		int ict = 0;
		int jct = 0;
		
		for(int i = this.XOffset; ict < 8; i += this.SquareSize)
		{
			for(int j = this.YOffset; jct < 8; j += this.SquareSize)
			{
				temp.add(new Rectangle(i, j, this.SquareSize, this.SquareSize));
				
				if(IsWhite(ict, jct))
					tempW.add(new Rectangle(i, j, this.SquareSize, this.SquareSize));
				
				else
					tempB.add(new Rectangle(i, j, this.SquareSize, this.SquareSize));
				
				jct++;
			}
			
			jct = 0;
			ict++;
		}
		
		this.AllRects  = temp;
		this.BlackRects = tempB;
		this.WhiteRects = tempW;
	}
	
	private boolean IsWhite(int x, int y)
	{
		return IsEven(x) == IsEven(y);
	}
	
	private boolean IsEven(int i)
	{
		return ((i % 2) == 0);
	}
	
	public Dimension getPreferredSize()
	{
		return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
	}
	
	public Dimension getMinimumSize()
	{
		return new Dimension(100, 100);
	}

}
